﻿[<AutoOpen>]
module WingBeats.CSS.Values

[<AutoOpen>]
module Common =
    let url str  = sprintf "url(%s)" str
    let inherit_ = "inherit"
    let auto     = "auto"
    let none     = "none"
    let normal   = "normal"

type BackgroundValues = 
    { Color       : string
      Image       : string
      Repeat      : string 
      Attachment  : string
      Position    : string * string}

type BorderValues =
    { Style : string
      Width : string
      Color : string }

type ListStyleValues =
    { Type     : string
      Position : string * string
      Image    : string }

type SystemFontValues =
    { Style   : string
      Variant : string
      Weight  : string
      Size    : string }

type FontValues =
    { Style      : string
      Variant    : string
      Weight     : string
      Size       : string
      LineHeight : string
      FontFamily : string } 

[<RequireQualifiedAccess>]
module FontFamily =
    let inherit_ = inherit_
    [<RequireQualifiedAccess>]
    module Generic =
        let serif     = "serif"
        let sansSerif = "sans-serif"
        let cursive   = "cursive"
        let fantasy   = "fantasy"
        let monospace = "monospace"
[<RequireQualifiedAccess>]
module FontStyle =
    let italic   = "italic"
    let oblique  = "oblique"
    let normal   = normal
[<RequireQualifiedAccess>]
module FontVariant =
    let smallCaps = "small-caps"
    let normal    = normal
[<RequireQualifiedAccess>]
module FontWeight =
    let normal   = normal
    let bold     = "bold"
    let bolder   = "bolder"
    let lighter  = "lighter"
    let _100     = "100"
    let _200     = "200"
    let _300     = "300"
    let _400     = "400"
    let _500     = "500"
    let _600     = "600"
    let _700     = "700"
    let _800     = "800"
    let _900     = "900"

[<RequireQualifiedAccess>]
module FontSize =
    [<RequireQualifiedAccess>]
    module Absolute =
        let xxSmall = "xx-small"
        let xSmall  = "x-small"
        let small   = "small"
        let medium  = "medium"
        let large   = "large"
        let xLarge  = "x-large"
        let xxLarge = "xx-large"

    [<RequireQualifiedAccess>]
    module Relative =
        let larger  = "larger"
        let smaller = "smaller"

[<RequireQualifiedAccess>]
module Align =
    let left   = "left"
    let right  = "right"
    let top    = "top"
    let bottom = "bottom"
    let center = "center"

[<RequireQualifiedAccess>]
module TextDecoration =
    let underline   = "underline"
    let overline    = "overline"
    let lineThrough = "line-through"
    let blink       = "blink"

[<RequireQualifiedAccess>]
module BackgroundRepeat =
    let repeat   = "repeat"
    let repeatX  = "repeat-x"
    let repeatY  = "repeat-y"
    let noRepeat = "no-repeat"

[<RequireQualifiedAccess>]
module BackgroundAttachment =
    let scroll = "scroll"
    let fixed_ = "fixed"

[<RequireQualifiedAccess>]
module ListStyleType =
    let disc               = "disc"
    let circle             = "circle"
    let square             = "square"
    let decimal            = "decimal"
    let decimalLeadingZero = "decimal-leading-zero"
    let lowerRoman         = "lower-roman"
    let upperRoman         = "upper-roman"
    let lowerGreek         = "lower-greek"
    let lowerLatin         = "lower-latin"
    let upperLatin         = "upper-latin"
    let armenian           = "armenian"
    let georgian           = "georgian"
    let lowerAlpha         = "lower-alpha"
    let upperAlpha         = "upper-alpha"
    let none               = none
    let inherit_           = inherit_

[<RequireQualifiedAccess>]
module BorderStyle =
    let hidden = "hidden"
    let dotted = "dotted"
    let dashed = "dashed"
    let solid  = "solid"
    let double = "double"
    let groove = "groove"
    let ridge  = "ridge"
    let inset  = "inset"
    let outset = "outset"
    let none   = none

[<AutoOpen>]
module Units =
    
    let zero = "0"

    /// CSS: Pixels, abbreviated px
    let px i = if i > 0 then sprintf "%ipx" i else zero

    let private floatUnit unitName number =
        if number > 0.0 
            then 
                let numberStr = (sprintf "%f" number).Trim('0').Trim('.')
                (sprintf "%s%s" numberStr unitName)
            else zero
    
    /// CSS: Font size, abbreviated em
    let em = floatUnit "em"
    /// CSS: X-height, abbreviated ex
    let ex = floatUnit "ex"
    /// CSS: Percentage, abbreviated %
    let percent = floatUnit "%"

    module Absolute =
        /// CSS: Inches, abbreviated in
        let inch = floatUnit "in"
        /// CSS: Centimeters, abbreviated cm
        let cm = floatUnit "cm"
        /// CSS: Millimeters, abbreviated mm
        let mm = floatUnit "mm"
        /// CSS: Points, abbreviated pt
        let pt = floatUnit "pt"
        /// CSS: Picas, abbreviated pc
        let pc = floatUnit "pc"

[<AutoOpen>]
module ColorValues =
    open System.Text.RegularExpressions

    exception ColorStringNotValid

    let private validRange value =
        let validRange_ i = if i < 0 then 0 elif i > 255 then 255 else i
        let r, g, b      = value
        (validRange_ r, validRange_ g, validRange_ b)

    type Color = | Rgb of int * int * int
                 member private this.values = match this with
                                              | Rgb (r, g, b) -> (r, g, b)

                 static member (-) (a : Color, b : Color) =
                    match a, b with
                    | (Rgb (r1, g1, b1)), (Rgb (r2, g2, b2)) 
                        -> Rgb (validRange (r1 - r2, g1 - g2, b1 - b2))
                 
                 static member (-) (a : Color, (r : int, g : int, b : int) ) =
                    match a with
                    | Rgb (aR, aG, aB) -> Rgb (validRange (aR - r, aG - g, aB - b))
                
                 static member (-) (a : Color, value : int) =
                    match a with
                    | Rgb (aR, aG, aB) -> Rgb (validRange (aR - value, aG - value, aB - value))

                 static member (+) (a : Color, b : Color) =
                    match a, b with
                    | (Rgb (r1, g1, b1)), (Rgb (r2, g2, b2)) 
                        -> Rgb (validRange (r1 + r2, g1 + g2, b1 + b2))    
                
                 static member (+) (a : Color, (r : int, g : int, b : int) ) =
                    match a with
                    | Rgb (aR, aG, aB) -> Rgb (validRange (aR + r, aG + g, aB + b))

                 static member (+) (a : Color, value : int) =
                    match a with
                    | Rgb (aR, aG, aB) -> Rgb (validRange (aR + value, aG + value, aB + value))

    let private toString func color = match color with
                                      | Rgb (r, g, b)
                                        -> let r, g, b = validRange (r, g, b)
                                           func r g b : string
    
    let toHexString = toString (fun r g b -> sprintf "#%02x%02x%02x" r g b)
    let toRgbString = toString (fun r g b -> sprintf "rgb(%i,%i,%i)" r g b)

    /// CSS: Turns the string into RGB value. Raises a ColorStringNotValid exception
    /// it the string is in the wrong format. valid formats are:
    ///   - "#0f23ff"
    ///   - "#fff"
    ///   - "rgb(23, 255, 0)
    ///
    let toRgb str = 
        let stringsToInts hex (s1, s2, s3) =
            let parse (str : string) =
                match hex with
                | true 
                    ->  let s = 
                            if str.Length = 1 then str + str else str
                        System.Int32.Parse(s, System.Globalization.NumberStyles.HexNumber)
                | false
                    ->  System.Int32.Parse(str)
            (parse s1, parse s2, parse s3)
        
        let regEx pattern str = Regex.Match (str, pattern)

        let result =

            let (|LongHex|ShortHex|Decimal|) (str : string) = 
                let strippedStr = str.Replace(" ", "")
                let rgbPattern = 
                    sprintf "rgb\\(%s,%s,%s\\)" 
                            "\\d{1,3}" 
                            "\\d{1,3}" 
                            "\\d{1,3}"
                
                let regExBool pattern = (regEx pattern strippedStr).Success

                if   (regExBool "#[0-9a-fA-F]{6}") then LongHex  strippedStr
                elif (regExBool "#[0-9a-fA-F]{3}") then ShortHex strippedStr
                elif (regExBool rgbPattern)        then Decimal  strippedStr
                else raise ColorStringNotValid

            match str with
            | LongHex  str -> (str.[1..2], str.[3..4], str.[5..6]) |> stringsToInts true
            | ShortHex str -> (str.[1..1], str.[2..2], str.[3..3]) |> stringsToInts true
            | Decimal  str -> let numbers = str.Replace("rgb(", "").Replace(")", "").Split(',')
                              (numbers.[0], numbers.[1], numbers.[2])
                              |> stringsToInts false
        Rgb (result |> validRange)


    let maroon  = Rgb (0x80,    0,    0)
    let red     = Rgb (0xff,    0,    0)
    let orange  = Rgb (0xff, 0xa5, 0x00)
    let yellow  = Rgb (0xff, 0xff,    0)
    let olive   = Rgb (0x80, 0x80,    0)
    let purple  = Rgb (0x80,    0, 0x80)
    let fuchsia = Rgb (0xff,    0, 0xff)
    let white   = Rgb (0xff, 0xff, 0xff)
    let lime    = Rgb (   0, 0xff,    0)
    let green   = Rgb (   0, 0x80,    0)
    let navy    = Rgb (   0,    0, 0x80)
    let blue    = Rgb (   0,    0, 0xff)
    let aqua    = Rgb (   0, 0xff, 0xff)
    let teal    = Rgb (   0, 0x80, 0x80)
    let black   = Rgb (   0,    0,    0)
    let silver  = Rgb (0xc0, 0xc0, 0xc0)
    let gray    = Rgb (0x80, 0x80, 0x80)
    
    let transparent = "transparent"
// end module Color